import { Matrix2D } from './Matrix2D';
import {
  type Point2D,
  type Transform2D,
  type TransformResult,
  TransformType,
  type Vector2D,
} from './types';

/**
 * 变换工具函数集合
 * 提供高级变换操作和便利方法
 */
export class TransformUtils {
  constructor() {
    throw new Error(
      'TransformUtils is a static class and should not be instantiated',
    );
  }

  /**
   * 对点应用变换并返回结果
   */
  static transformPoint(
    point: Point2D,
    matrix: Transform2D,
    type: TransformType = TransformType.MATRIX,
    description?: string,
  ): TransformResult<Point2D> {
    const matrix2d = new Matrix2D({ transform: matrix });
    const transformed = matrix2d.transformPoint(point);

    return {
      original: { ...point },
      transformed,
      matrix: { ...matrix },
      type,
    };
  }

  /**
   * 对向量应用变换并返回结果
   */
  static transformVector(
    vector: Vector2D,
    matrix: Transform2D,
    type: TransformType = TransformType.MATRIX,
    description?: string,
  ): TransformResult<Vector2D> {
    const matrix2d = new Matrix2D({ transform: matrix });
    const transformed = matrix2d.transformVector(vector);

    return {
      original: { ...vector },
      transformed,
      matrix: { ...matrix },
      type,
    };
  }

  /**
   * 对点集合应用变换并返回结果
   */
  static transformPoints(
    points: Point2D[],
    matrix: Transform2D,
    type: TransformType = TransformType.MATRIX,
    description?: string,
  ): TransformResult<Point2D[]> {
    const matrix2d = new Matrix2D({ transform: matrix });
    const transformed = matrix2d.transformPoints(points);

    return {
      original: [...points],
      transformed,
      matrix: { ...matrix },
      type,
    };
  }

  /**
   * 对向量集合应用变换并返回结果
   */
  static transformVectors(
    vectors: Vector2D[],
    matrix: Transform2D,
    type: TransformType = TransformType.MATRIX,
    description?: string,
  ): TransformResult<Vector2D[]> {
    const matrix2d = new Matrix2D({ transform: matrix });
    const transformed = matrix2d.transformVectors(vectors);

    return {
      original: [...vectors],
      transformed,
      matrix: { ...matrix },
      type,
    };
  }

  /**
   * 创建组合变换矩阵
   */
  static composeTransforms(
    transforms: Array<{
      type: TransformType;
      matrix: Transform2D;
      origin?: Point2D;
    }>,
  ): Transform2D {
    const matrix2d = new Matrix2D();

    transforms.forEach(({ matrix, origin }) => {
      matrix2d.multiply(matrix, origin);
    });

    return matrix2d.getMatrix();
  }

  /**
   * 创建围绕指定点的旋转变换
   * 注意：由于y轴方向已改为向上，正角度表示顺时针旋转
   */
  static createRotationAroundPoint(
    angle: number,
    center: Point2D,
  ): Transform2D {
    const matrix2d = new Matrix2D();
    matrix2d.translate(-center.x, -center.y);
    matrix2d.rotate(angle);
    matrix2d.translate(center.x, center.y);

    return matrix2d.getMatrix();
  }

  /**
   * 创建围绕指定点的缩放变换
   */
  static createScalingAroundPoint(
    sx: number,
    sy: number,
    center: Point2D,
  ): Transform2D {
    const matrix2d = new Matrix2D();
    matrix2d.translate(-center.x, -center.y);
    matrix2d.scale(sx, sy);
    matrix2d.translate(center.x, center.y);

    return matrix2d.getMatrix();
  }

  /**
   * 创建反射变换（相对于直线）
   */
  static createReflection(
    lineAngle: number, // 直线角度（弧度）
    lineDistance: number = 0, // 直线到原点的距离
  ): Transform2D {
    const cos2 = Math.cos(2 * lineAngle);
    const sin2 = Math.sin(2 * lineAngle);

    const matrix2d = new Matrix2D();

    if (lineDistance !== 0) {
      // 如果直线不经过原点，需要先平移
      const perpendicularAngle = lineAngle + Math.PI / 2;
      const dx = lineDistance * Math.cos(perpendicularAngle);
      const dy = lineDistance * Math.sin(perpendicularAngle);

      matrix2d.translate(-dx, -dy);
    }

    // 反射矩阵
    matrix2d.multiply({
      a: cos2,
      b: sin2,
      c: sin2,
      d: -cos2,
      e: 0,
      f: 0,
    });

    if (lineDistance !== 0) {
      const perpendicularAngle = lineAngle + Math.PI / 2;
      const dx = lineDistance * Math.cos(perpendicularAngle);
      const dy = lineDistance * Math.sin(perpendicularAngle);

      matrix2d.translate(dx, dy);
    }

    return matrix2d.getMatrix();
  }

  /**
   * 创建仿射变换（6个参数）
   */
  static createAffineTransform(
    a: number,
    b: number,
    c: number,
    d: number,
    e: number,
    f: number,
  ): Transform2D {
    return { a, b, c, d, e, f };
  }

  /**
   * 插值两个变换矩阵
   */
  static interpolateMatrices(
    start: Transform2D,
    end: Transform2D,
    t: number,
  ): Transform2D {
    return {
      a: start.a + (end.a - start.a) * t,
      b: start.b + (end.b - start.b) * t,
      c: start.c + (end.c - start.c) * t,
      d: start.d + (end.d - start.d) * t,
      e: start.e + (end.e - start.e) * t,
      f: start.f + (end.f - start.f) * t,
    };
  }

  /**
   * 分解变换矩阵为基本变换
   */
  static decomposeMatrix(matrix: Transform2D): {
    translation: { tx: number; ty: number };
    rotation: number;
    scale: { sx: number; sy: number };
    skew: { shx: number; shy: number };
  } {
    const { a, b, c, d, e, f } = matrix;

    // 提取平移
    const translation = { tx: e, ty: f };

    // 计算缩放和旋转
    const det = a * d - b * c;
    const scaleX = Math.sqrt(a * a + b * b);
    const scaleY = Math.abs(det) / scaleX;

    // 计算旋转角度
    const rotation = Math.atan2(b, a);

    // 计算倾斜 - 先移除旋转和缩放的影响
    // 构建旋转和缩放的逆变换矩阵
    const cos = Math.cos(rotation);
    const sin = Math.sin(rotation);
    
    // 移除旋转和缩放后的矩阵元素
    const aNoRotation = (a * cos + b * sin) / scaleX;
    const bNoRotation = (-a * sin + b * cos) / scaleX;
    const cNoRotation = (c * cos + d * sin) / scaleY;
    const dNoRotation = (-c * sin + d * cos) / scaleY;
    
    // 计算倾斜因子
    const shx = Math.atan2(cNoRotation, dNoRotation);
    const shy = Math.atan2(bNoRotation, aNoRotation);

    return {
      translation,
      rotation,
      scale: { sx: scaleX, sy: scaleY },
      skew: { shx, shy },
    };
  }

  /**
   * 检查两个点是否相等（考虑浮点数误差）
   */
  static pointsEqual(
    p1: Point2D,
    p2: Point2D,
    epsilon: number = 1e-10,
  ): boolean {
    return Math.abs(p1.x - p2.x) < epsilon && Math.abs(p1.y - p2.y) < epsilon;
  }

  /**
   * 检查两个矩阵是否相等（考虑浮点数误差）
   */
  static matricesEqual(
    m1: Transform2D,
    m2: Transform2D,
    epsilon: number = 1e-10,
  ): boolean {
    return (
      Math.abs(m1.a - m2.a) < epsilon &&
      Math.abs(m1.b - m2.b) < epsilon &&
      Math.abs(m1.c - m2.c) < epsilon &&
      Math.abs(m1.d - m2.d) < epsilon &&
      Math.abs(m1.e - m2.e) < epsilon &&
      Math.abs(m1.f - m2.f) < epsilon
    );
  }

  /**
   * 计算两点之间的距离
   */
  static distance(p1: Point2D, p2: Point2D): number {
    const dx = p2.x - p1.x;
    const dy = p2.y - p1.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  /**
   * 计算向量的长度
   */
  static vectorLength(vector: Vector2D): number {
    return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
  }

  /**
   * 标准化向量
   */
  static normalizeVector(vector: Vector2D): Vector2D {
    const length = TransformUtils.vectorLength(vector);
    if (length === 0) return { x: 0, y: 0 };
    return { x: vector.x / length, y: vector.y / length };
  }

  /**
   * 计算两个向量的点积
   */
  static dotProduct(v1: Vector2D, v2: Vector2D): number {
    return v1.x * v2.x + v1.y * v2.y;
  }

  /**
   * 计算两个向量的叉积（返回标量）
   */
  static crossProduct(v1: Vector2D, v2: Vector2D): number {
    return v1.x * v2.y - v1.y * v2.x;
  }

  /**
   * 计算两点之间的中点
   */
  static midpoint(p1: Point2D, p2: Point2D): Point2D {
    return {
      x: (p1.x + p2.x) / 2,
      y: (p1.y + p2.y) / 2,
    };
  }

  /**
   * 计算点集合的质心
   */
  static centroid(points: Point2D[]): Point2D {
    if (points.length === 0) return { x: 0, y: 0 };

    const sum = points.reduce(
      (acc, point) => ({
        x: acc.x + point.x,
        y: acc.y + point.y,
      }),
      { x: 0, y: 0 },
    );

    return {
      x: sum.x / points.length,
      y: sum.y / points.length,
    };
  }

  /**
   * 创建测试数据点集合
   */
  static createTestPoints(
    count: number = 10,
    bounds = { x: 100, y: 100 },
  ): Point2D[] {
    const points: Point2D[] = [];
    for (let i = 0; i < count; i++) {
      points.push({
        x: Math.random() * bounds.x - bounds.x / 2,
        y: Math.random() * bounds.y - bounds.y / 2,
      });
    }
    return points;
  }

  /**
   * 创建测试向量集合
   */
  static createTestVectors(
    count: number = 10,
    maxLength: number = 50,
  ): Vector2D[] {
    const vectors: Vector2D[] = [];
    for (let i = 0; i < count; i++) {
      const angle = Math.random() * Math.PI * 2;
      const length = Math.random() * maxLength;
      vectors.push({
        x: Math.cos(angle) * length,
        y: Math.sin(angle) * length,
      });
    }
    return vectors;
  }
}
